package com.itheima.activities.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.itheima.activities.mapper.RedPacketListMapper;
import com.itheima.activities.mapper.RedPacketMapper;
import com.itheima.activities.service.RedPacketService;
import com.itheima.common.ThreadLocalUtil;
import com.itheima.entity.dto.RedPacketDto;
import com.itheima.entity.pojo.RedPacket;
import com.itheima.entity.pojo.RedPacketList;
import com.itheima.entity.vo.RedPacketVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xml.sax.Locator;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author :wujunjie
 * createTime :2022-12-19 15:52
 * description :
 */
@Service
public class RedPacketServiceImpl implements RedPacketService {
    @Autowired
    private RedPacketMapper redPacketMapper;
    @Autowired
    private RedPacketListMapper redPacketListMapper;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Override
    @Transactional(rollbackForClassName = {"RuntimeException"})
    public Boolean insertRedPacket(RedPacketDto redPacketDto) {
        RedPacket redPacket = new RedPacket();
        BeanUtils.copyProperties(redPacketDto,redPacket);
        redPacket.setId(IdUtil.getSnowflakeNextIdStr());
        redPacket.setStatus(0);
        Boolean insertRedPacket=redPacketMapper.insertRedPacket(redPacket);

        BigDecimal totalMoney = redPacket.getTotalMoney();
        ArrayList<RedPacketList> redPacketLists = new ArrayList<>();
        BigDecimal min = new BigDecimal(0);
        BigDecimal load_Factor = new BigDecimal(2);
        Integer amount = redPacket.getAmount();
        for (int i = 0; i < amount; i++) {
            BigDecimal money;
            if(i<amount-1){
                 money = RandomUtil.randomBigDecimal(min,totalMoney.divide(load_Factor));
            }else {
                money=totalMoney;
            }
            RedPacketList redPacketList = new RedPacketList();
            redPacketList.setMoney(money);
            totalMoney = totalMoney.subtract(money);
            redPacketList.setId(IdUtil.getSnowflakeNextIdStr());
            redPacketList.setStatus(0);
            redPacketList.setRedPacketId(redPacket.getId());
            redPacketList.setCreateTime(LocalDateTime.now());
            redPacketLists.add(redPacketList);
        }
        Boolean insertRedPacketLists=redPacketListMapper.insertRedPacketLists(redPacketLists);
        return insertRedPacket&&insertRedPacketLists;
    }

    @Override
    public List<RedPacketList> selectRedPacketListsByStatusAndTime() {

        LocalDateTime now = LocalDateTime.now();
        List<RedPacket> redPackets=redPacketMapper.selectRedPacketByStatusAndTime(1,now);
        if (redPackets.size()==0){
            return null;
        }
        Set<String> redPacketIds = redPackets.stream().map(RedPacket::getId).collect(Collectors.toSet());
        return redPacketListMapper.selectRedPacketListByRedPacketIdsAndStatus(redPacketIds,0);
    }

    @Override
    public Boolean updateRedPacketListStatus(List<RedPacketList> redPacketLists) {
        Set<String> ids = redPacketLists.stream().map(RedPacketList::getRedPacketId).collect(Collectors.toSet());
        return redPacketListMapper.updateStatusByRedPacketIds(ids);
    }

    @Override
    public RedPacketList getRedPacketList(String redPacketId) {
        Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(redPacketId + ThreadLocalUtil.getId(), LocalDateTime.now().toString(), 1, TimeUnit.HOURS);
        if (!setIfAbsent){
            throw new RuntimeException("请不要重复操作");
        }

        String s = redisTemplate.opsForList().rightPop("redPacketId:" + redPacketId);

        if (StringUtils.isEmpty(s)){
            //todo 这里可以用redis做优化
            String result = redisTemplate.opsForValue().get("redPacketId");
            if (!StringUtils.isEmpty(result)){
                throw new RuntimeException("活动已结束");
            }
            RedPacket redPacket=redPacketMapper.selectRedPacketById(redPacketId);
            if (!ObjectUtil.isEmpty(redPacket)&&redPacket.getStatus()==2){
                redisTemplate.opsForValue().set(redPacketId, LocalDateTime.now().toString());
            }
            if (!ObjectUtil.isEmpty(redPacket)&&redPacket.getStatus()==1){
                Set<String> ids=new HashSet<>();
                ids.add(redPacketId);
                Boolean aBoolean = updateRedPacketStatus(ids,2);
                if (!aBoolean){
                    throw new RuntimeException("状态更新失败");
                }
            }
            throw new RuntimeException("该活动数据为空");
        }
        RedPacketList redPacketList = JSON.parseObject(s, RedPacketList.class);
        redPacketList.setUserId(ThreadLocalUtil.getId());
        redPacketList.setStatus(2);
        rabbitTemplate.convertAndSend("redPacketListUpdate.direct","redPacketList",JSON.toJSONString(redPacketList));
        return redPacketList;
    }

    @Override
    public List<RedPacketVo> getAllRedPacket() {
        List<RedPacket> redpacket =redPacketMapper.selectAll();
        if (redpacket.size()==0){
            return null;
        }
        return BeanUtil.copyToList(redpacket, RedPacketVo.class);
    }

    @Override
    public List<RedPacket> selectRedPacketByStatusAndTime() {
        return redPacketMapper.selectRedPacketByStatusAndTime(0,LocalDateTime.now());
    }

    @Override
    public Boolean updateRedPacketStatus(Set<String> ids, Integer status) {

        return redPacketMapper.updateByIds(ids,status);
    }
}
